using System;
using System.IO;
using System.Diagnostics;
using System.ServiceProcess;
using Subsync.Core.Sync;

namespace Subsync.Core.SvnServe
{
	/// <summary>
	/// Author: Steven Goh Jian Wen
	/// </summary>
	public class SvnServe
	{
		//properties
		PlatformID OS {
			get {
				return pid;
			}
		}
		
		bool IsWindows {
			get {
				switch (pid) {
				case PlatformID.Win32Windows:
				case PlatformID.Win32NT:
					return true;
				default:
					return false;
				}
			}
		}
		
		//helpers
		Config config = new Config ();
		OperatingSystem os = Environment.OSVersion;
		PlatformID pid;
		
		//default
		private Process svnserve = new Process ();
		private string filename = "svnserve";
		
		//windows
		string service_name = "SVNService";
		string service_desc = "Subsync SVN Repository Server";
		ServiceController svnserve_service;
		string daemon_args;
		

		public SvnServe ()
		{
			//initialization
			pid = os.Platform;
			
			if (IsWindows)
				InstantiateWindows ();								
			else
				InstantiateDefault ();
			
		}

		public bool IsActive ()
		{
			if (IsWindows) {
				svnserve_service = new ServiceController (service_name);
				if (IsServiceInstalled ())
					if (svnserve_service.Status == ServiceControllerStatus.Running)
						return true;
			} else {
				try {
					Process[] processes = Process.GetProcessesByName (filename);
					if (processes.Length > 0)
						return true;
				} catch (InvalidOperationException) {
					throw new ThrottledException ("Please try again.");
				}
			}
			return false;
		}
		
		/// <summary>
		/// SvnServe Toggle. If its active, it will
		/// </summary>
		public void Execute ()
		{
			if (IsActive ())
				Stop ();
			else
				Start ();
		}

		public void Start ()
		{
			if (!IsActive ()) {
				if (IsWindows) {
					if (!IsWindowsSetuped ())
						throw new SVNServeNotSetuped ("SVNServe for Windows is not setup.");
					try {
						svnserve_service = new ServiceController (service_name);
						svnserve_service.Start ();
						svnserve_service.WaitForStatus (ServiceControllerStatus.Running, new System.TimeSpan (50));
					} catch (InvalidOperationException) {
						throw new UnableToStartSVNServe ("SvnServe Service cannot be started");
					} catch (System.ServiceProcess.TimeoutException) {
						throw new UnableToStartSVNServe ("SvnServe Service cannot be started");
					}
				} else {
					if (!IsOthersSetuped ())
						throw new SVNServeNotSetuped ("SVNServe is not setup.");
					svnserve.Start ();
				}
			}
		}

		public void Stop ()
		{
			if (IsActive ()) {
				if (IsWindows) {
					if (!IsWindowsSetuped ())
						throw new SVNServeNotSetuped ("SVNServe for Windows is not setup.");
					svnserve_service = new ServiceController (service_name);
					svnserve_service.Stop ();
				} else {
					if (!IsOthersSetuped ())
						throw new SVNServeNotSetuped ("SVNServe is not setup.");
					Process[] processes = Process.GetProcessesByName (filename);
					foreach (Process p in processes) {
						int pid = p.Id;
						OsKill (pid);
					}
					
				}
			}
		}
		
		/// <summary>
		/// Check if svnserve is installed (properly) on a Windows host
		/// </summary>
		public bool IsServiceInstalled ()
		{
			if (IsWindows) {
				ServiceController[] services = ServiceController.GetServices ();
				foreach (ServiceController service in services) {
					if (service.ServiceName == service_name)
						return true;
				}
				return false;
			}
			return true;
		}

		/// <summary>
		/// Install a windows service for the svnserve path provided
		/// We will do this the shell way.
		/// </summary>
		/// <param name="path">
		/// A <see cref="System.String"/>
		/// </param>
		public void InstallWindowsService (string svnserve_path)
		{
			
			//ServiceInstaller
			if (!IsWindows) {
				return;
			}
			
			if (File.Exists(config.GetProperty ("SVNServePath"))) {
				//uninstall if an existing service exists
				UninstallWindowsService ();
				//install a new one
				string shell_file = "sc";
				string shell_args = "create \"" + service_name + "\" binpath= \"" + config.GetProperty ("SVNServePath") + " " + daemon_args + "\" displayname= \"" + service_desc + "\" depend= Tcpip start= demand";
				Console.WriteLine (shell_file + " " + shell_args);
				Process install_service = new Process ();
				install_service.StartInfo.CreateNoWindow = true;
				install_service.StartInfo.FileName = shell_file;
				install_service.StartInfo.UseShellExecute = true;
				install_service.StartInfo.Arguments = shell_args;
				install_service.Start ();
			}
		}
		
		/// <summary>
		/// Uninstall the SVNServe service if it exists
		/// </summary>
		public void UninstallWindowsService ()
		{
			if ((IsWindows) && (IsServiceInstalled())) {
				string shell_file = "sc";
				string shell_args = "delete " + service_name;
				Process uninstall_service = new Process ();
				uninstall_service.StartInfo.CreateNoWindow = true;
				uninstall_service.StartInfo.FileName = shell_file;
				uninstall_service.StartInfo.UseShellExecute = true;
				uninstall_service.StartInfo.Arguments = shell_args;
				uninstall_service.Start ();
			}
		}
		
		void OsKill (int process_id)
		{
			OperatingSystem os = Environment.OSVersion;
			PlatformID pid = os.Platform;
			switch (pid) {
			case PlatformID.Unix:
				Process kill = new Process ();
				kill.StartInfo.CreateNoWindow = true;
				kill.StartInfo.FileName = "kill";
				kill.StartInfo.UseShellExecute = true;
				kill.StartInfo.Arguments = process_id.ToString ();
				kill.Start ();
				break;
			default:
				Process p = Process.GetProcessById (process_id);
				p.Kill ();
				break;
			}
		}
		
		void InstantiateWindows ()
		{
			svnserve_service = new ServiceController (service_name);
			
			//instantiate vars for service install
			daemon_args = "--service -r \\\"" + config.GetProperty ("RepoStoragePath") + "\\\"";
		}
		
		void InstantiateDefault ()
		{
			if (IsOthersSetuped()) {
				svnserve.StartInfo.FileName = config.GetProperty ("SVNServePath");
				svnserve.StartInfo.CreateNoWindow = true;
				svnserve.StartInfo.Arguments = "-d -r " + config.GetProperty ("RepoStoragePath");
				filename = Path.GetFileName (config.GetProperty ("SVNServePath"));
			}
		}

		bool IsOthersSetuped ()
		{
			if (File.Exists (config.GetProperty ("SVNServePath")))
				return true;
			return false;
		}
		
		bool IsWindowsSetuped ()
		{
			if (File.Exists (config.GetProperty ("SVNServePath")))
				return true;
			return false;
		}
		
	}
}
